<?php

namespace Hilaoyu\ComputeCloudClient\Api;

use Hilaoyu\ComputeCloudClient\AssetModel;
use Hilaoyu\ComputeCloudClient\AssetModels;
use Hilaoyu\ComputeCloudClient\ctyun\CtyunClientVpc;
use Hilaoyu\ComputeCloudClient\CtyunApi;
use Hilaoyu\ComputeCloudClient\Interfaces\ApiVpcInterface;
use Hilaoyu\Utils\UtilArr;
use Hilaoyu\Utils\UtilStr;

/**
 * Class CtyunVpc
 * @package Hilaoyu\ComputeCloudClient\Api
 * @property  CtyunClientVpc $cliepnt
 * @property CtyunApi api
 */
class CtyunVpc extends CtyunApiBase implements ApiVpcInterface
{

    public function regionsDescribe( ):array
    {
        return $this->api->ecs()->regionsDescribe();
    }
    public function zonesDescribe($regionId=''):array
    {
        return $this->api->ecs($regionId)->zonesDescribe();
    }

    public function vpcsDescribe(string $vpcId = null, string $vpcname = null, $regionId = ''): AssetModels
    {

        $regionId = $this->getRegionId($regionId);
        $models = new AssetModels();
        try {

            $data = $this->client->listVpcs((string)$vpcId,(string)$vpcname,(string)$regionId);
            if (is_null($data)) {
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            foreach ($data as $datum) {
                $id = UtilArr::get($datum,"vpcID");
                if (!$id) {
                    continue;
                }

                $model = new AssetModel();
                $model->type = 'vpc';
                $model->asset_id = $id;
                $model->region_id = $regionId;
                $model->name = UtilArr::get($datum,"name");
                $model->network = UtilArr::get($datum,"CIDR");
                $model->status = "Available";


                $models->push($model);
            }

        }  catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return $models;
    }

    public function vpcDescribe(string $vpcId, $regionId = ''): ?AssetModel
    {
        $models = $this->vpcsDescribe($vpcId, null, $regionId);
        if (!$models) {
            return null;
        }

        return $models->first(function ($item) use ($vpcId) {
            return $item->asset_id == $vpcId;
        });
    }

    public function vpcCreate($vpcname = null, $network = null, $ipv6Network = null, $regionId = '', $resourceGroupId = null): ?AssetModel
    {
        $regionId = $this->getRegionId($regionId);

        try {
            if ($vpcname) {
                $hasVpcs = $this->vpcsDescribe("",$vpcname, $regionId);

                if ($hasVpcs && $hasVpcs->count() > 0) {
                    throw new \Exception('vpcname is exists');
                }
            }


            $vpcId = $this->client->createVpc((string)$vpcname,(string)$network,(string)$ipv6Network,$regionId);

            if (!$vpcId) {
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            $datum = $this->vpcDescribe($vpcId);

            return $this->vpcDescribe($vpcId);
        } catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }

        return null;
    }

    public function vpcDelete($vpcId, $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        $vpcId = trim($vpcId);
        if (!$vpcId) {
            return false;
        }
        if (!$this->vpcDescribe($vpcId)) {
            return true;
        }

        try {

            $result = $this->client->deleteVpc($vpcId);
            if(!$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }

            return (bool)$result;
        }  catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;
    }

    public function vSwitchCreate($vpcId, $zoneId, $network, $vSwitchName = null, $ipv6Network = null, $regionId = ''): ?AssetModel
    {
        $regionId = $this->getRegionId($regionId);
        $zoneId = trim($zoneId);
        $vpcId = trim($vpcId);
        $vSwitchName = trim($vSwitchName);
        if (!$vSwitchName) {
            $vSwitchName = UtilStr::randString(8);
        }

        if (!$vpcId) {
            return null;
        }
        try {

            $id = $this->client->createSubnet($vpcId,$network,(string)$vSwitchName,(string)$ipv6Network,$regionId);
            if(!$id){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }

            return $this->vSwitchDescribe($id);
        }  catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return null;
    }

    public function vSwitchDelete($vSwitchId, $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        $vSwitchId = trim($vSwitchId);

        if (!$vSwitchId) {
            return false;
        }
        $vSwitch = $this->vSwitchDescribe($vSwitchId);
        if (!$vSwitch) {
            return true;
        }
        try {
            $result = $this->client->deleteSubnet($vSwitchId,$regionId);
            if(!$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            return (bool)$result;
        }  catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;

    }

    public function vSwitchsDescribe($vSwitchId = null, $vSwitchName = null, $vpcId = null, $regionId = ''): AssetModels
    {
        $regionId = $this->getRegionId($regionId);
        $models = new AssetModels();
        try {

            $data = $this->client->listSubnets((string)$vpcId,(string)$vSwitchId,(string)$regionId);

            if(!$data){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }

            foreach ($data as $datum) {
                $id = UtilArr::get($datum,"subnetID");
                if (!$id) {
                    continue;
                }

                $model = new AssetModel();
                $model->type = 'vsw';
                $model->asset_id = $id;
                $model->region_id = (string)$regionId;
                $model->name = UtilArr::get($datum,"name");
                $model->bind_asset_id = UtilArr::get($datum,"vpcID");
                $model->network = UtilArr::get($datum,"CIDR");
                $model->status = "Available";
                $model->vsw_vtb = "";


                $models->push($model);
            }

        }  catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return $models;
    }

    public function vSwitchDescribe($vSwitchId, $regionId = ''): ?AssetModel
    {
        $models = $this->vSwitchsDescribe($vSwitchId, "", "", $regionId);
        if (!$models) {
            return null;
        }

        return $models->first(function ($item) use ($vSwitchId) {
            return $item->asset_id == $vSwitchId;
        });

    }



    //EIP

    /**
     * @param int $bandwidth EIP的带宽峰值，取值范围：1~200，单位为Mbps
     * @param string $instanceChargeType EIP的计费方式，取值： PrePaid：包年包月。 PostPaid（默认值）：按量计费。; 当InstanceChargeType取值为PrePaid时，InternetChargeType必须取值PayByBandwidth；当InstanceChargeType取值为PostPaid时，InternetChargeType可取值PayByBandwidth或PayByTraffic
     * @param string $internetChargeType EIP的计量方式，取值： PayByBandwidth（默认值）：按带宽计费。 PayByTraffic：按流量计费。; 当InstanceChargeType取值为PrePaid时，InternetChargeType必须取值PayByBandwidth。;当InstanceChargeType取值为PostPaid时，InternetChargeType可取值PayByBandwidth或PayByTraffic。
     * @param string $isp 线路类型 BGP：BGP（多线）线路。BGP_PRO：BGP（多线）精品线路
     * @param string $pricingCycle 包年包月的计费周期，取值：Month（默认值）：按月付费。Year：按年付费;当InstanceChargeType参数的值为PrePaid时，该参数必选；当InstanceChargeType参数的值为PostPaid时，该参数可不填
     * @param int $period 购买时长。 当PricingCycle取值Month时，Period取值范围为1~9。  当PricingCycle取值Year时，Period取值范围为1~5。  如果InstanceChargeType参数的值为PrePaid时，该参数必选，如果InstanceChargeType参数的值为PostPaid时，该参数不填。
     * @param string $regionId
     * @param null $resourceGroupId 资源组ID
     * @return AssetModel
     */
    public function eipAddressAllocate(int $bandwidth = 50, string $instanceChargeType = 'PostPaid', string $internetChargeType = 'PayByTraffic', string $isp = 'BGP', string $pricingCycle = '', int $period = null, string $name = null, $regionId = '', $resourceGroupId = null): ?AssetModel
    {
        $regionId = $this->getRegionId($regionId);
        $bandwidth = intval($bandwidth);
        try {
            $cycleType = 'on_demand';
            if("PostPaid" != $instanceChargeType){
                $cycleType = $pricingCycle;
            }
            $demandBillingType = $this->formatEipChargeType($internetChargeType);

            $id = $this->client->createPublicip($bandwidth,$cycleType,$demandBillingType,$this->formatIsp($isp),intval($period),$name,$regionId);

            if (!$id) {
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }


            return $this->eipAddressDescribe($id);
        } catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return null;
    }

    public function eipAddressUpdate(string $allocationId,string $name=null,int $bandwidth=50,$regionId = ''):?AssetModel{
        $regionId = $this->getRegionId($regionId);
        $name = trim($name);
        $bandwidth = intval($bandwidth);
        try {
            if (!$model = $this->eipAddressDescribe($allocationId,null, $regionId)) {
                throw new \Exception('eip不存在');
            }


            $result = $this->client->updatePublicip($allocationId,(string)$name,$bandwidth,$regionId);

            if(!(bool)$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }

            return $this->eipAddressDescribe($allocationId);
        } catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return null;
    }

    public function eipAddressRelease(string $allocationId, $regionId = ''): bool
    {
        if (!$this->eipAddressDescribe($allocationId,null, $regionId)) {
            return true;
        }
        $regionId = $this->getRegionId($regionId);
        try {

            $result = $this->client->deletePublicip($allocationId);
            if(!(bool)$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            return (bool)$result;
        }  catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;
    }


    public function eipAddressAssociate(string $allocationId, string $instanceId, string $instanceType = null, $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        try {
            $result = $this->client->associatePublicip((string)$allocationId,(string)$instanceId,(string)$instanceType,$regionId);
            if(!(bool)$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            return (bool)$result;
        }  catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;
    }

    public function eipAddressUnassociate(string $allocationId, string $instanceId = null, $regionId = ''): bool
    {
        try {
            $result = $this->client->disassociatePublicip((string)$allocationId,$regionId);
            if(!(bool)$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            return (bool)$result;
        }  catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;
    }


    public function eipAddressesDescribe(array $allocationIds = [], string $eipAddress = null, string $status = null, $regionId = ''): AssetModels
    {
        $regionId = $this->getRegionId($regionId);
        $models = new AssetModels();
        try {

            $data = $this->client->listPublicips($allocationIds,(string)$eipAddress,$regionId);

            if(is_null($data)){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }

            foreach ($data as $datum) {
                $id = UtilArr::get($datum,"ID");
                if (!$id) {
                    continue;
                }

                $model = new AssetModel();
                $model->type = 'eip';
                $model->asset_id = $id;
                $model->region_id = (string)$regionId;
                $model->primary_ip = UtilArr::get($datum,"eipAddress");
                $model->name = $model->primary_ip;
                $model->bind_asset_id = UtilArr::get($datum,"associationID");


                $model->os_type = 'eni';
                $model->instance_type = UtilArr::get($datum,"associationType");
                $model->bandwidth = UtilArr::get($datum,"bandwidth");
                $model->status = $this->formatEipStatus(UtilArr::get($datum,"status"));
                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return $models;
    }

    public function eipAddressDescribe(string $allocationId, string $eipAddress = null, $regionId = ''): ?AssetModel
    {
        $models = $this->eipAddressesDescribe([$allocationId], $eipAddress, null, $regionId);
        if (!$models) {
            return null;
        }

        return $models->first(function ($item) use ($allocationId,$eipAddress) {
            return $item->asset_id == $allocationId || $item->primary_ip == $eipAddress;
        });

    }


    //路由表
    public function routeTableCreate($vpcId, $routeTableName = null, $regionId = ''): ?AssetModel
    {
        $regionId = $this->getRegionId($regionId);
        $vpcId = trim($vpcId);
        $routeTableName = trim($routeTableName);
        if(!$routeTableName){
            $routeTableName = UtilStr::randString(8);
        }

        if (!$vpcId) {
            return null;
        }
        try {

            $id = $this->client->createRouteTable($vpcId,$routeTableName,$regionId);
            if (!$id) {
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }

            $model = new AssetModel();
            $model->type = 'vtb';
            $model->asset_id = $id;
            $model->region_id = (string)$regionId;
            $model->name = (string)$routeTableName;

            $model->os_type = 'Custom';
            $model->status = 'AvailableNotUse';
            return $model;
        }catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return null;
    }

    public function routeTableDelete($routeTableId, $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        $routeTableId = trim($routeTableId);

        if (!$routeTableId) {
            return false;
        }

        if (!$this->routeTableDescribe($routeTableId)) {
            return true;
        }
        try {
            $result = $this->client->deleteRouteTable($routeTableId,$regionId);
            if(!$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            return (bool)$result;
        }catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;
    }

    public function routeTablesDescribe(string $routeTableId = null, string $vpcId = null, $regionId = ''): AssetModels
    {
        $regionId = $this->getRegionId($regionId);
        $models = new AssetModels();
        try {

            $data = $this->client->listRouteTables((string)$routeTableId,(string)$vpcId,$regionId);

            if (is_null($data)) {
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }

            foreach ($data as $datum) {
                $id = UtilArr::get($datum,"id");
                if (!$id) {
                    continue;
                }

                $model = new AssetModel();
                $model->type = 'vtb';
                $model->asset_id = $id;
                $model->region_id = (string)$regionId;
                $model->name = UtilArr::get($datum,"name");
                $model->os_type = "default"==UtilArr::get($datum,"origin") ? 'System' : 'Custom';
                $model->status = 'AvailableNotUse';
                $models->push($model);
            }

        } catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return $models;
    }

    public function routeTableDescribe(string $routeTableId, $regionId = ''): ?AssetModel
    {
        $models = $this->routeTablesDescribe($routeTableId, null, $regionId);
        if (!$models) {
            return null;
        }

        return $models->first(function ($item) use ($routeTableId) {
            return $item->asset_id == $routeTableId;
        });

    }

    public function routeTableAssociate(string $routeTableId, string $vSwitchId, $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        $routeTableId = trim($routeTableId);
        $vSwitchId = trim($vSwitchId);
        if (!$routeTableId || !$vSwitchId) {
            return false;
        }
        try {
            $result = $this->client->associateRouteTable($routeTableId,$vSwitchId,$regionId);
            if(!$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            return (bool)$result;
        } catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;
    }

    public function routeTableUnAssociate(string $routeTableId, string $vSwitchId, $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        $routeTableId = trim($routeTableId);
        $vSwitchId = trim($vSwitchId);
        if (!$routeTableId || !$vSwitchId) {
            return false;
        }
        try {
            $result = $this->client->disassociateRouteTable($vSwitchId,$regionId);
            if(!$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            return (bool)$result;
        }catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;
    }


    //todo ----
    public function routeEntryCreate(string $routeTableId, string $destinationNetwork, string $nextHopId, $nextHopType = 'Instance', $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        $routeTableId = trim($routeTableId);
        $destinationNetwork = trim($destinationNetwork);
        $nextHopId = trim($nextHopId);
        $nextHopType = trim($nextHopType);
        if (!$routeTableId || !$destinationNetwork || !$nextHopId) {
            return false;
        }
        try {

            $result = $this->client->createRouteTableRule($routeTableId,$destinationNetwork,$nextHopId,$this->formatnextHopType($nextHopType),$regionId);
            if(!$result){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }
            return (bool)$result;
        } catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;
    }

    public function routeEntryDelete(string $routeTableId, string $destinationNetwork = null, $regionId = ''): bool
    {
        $regionId = $this->getRegionId($regionId);
        $routeTableId = trim($routeTableId);
        if (!$routeTableId) {
            return false;
        }
        try {
            $rules = $this->routeEntryListDescribe($routeTableId);
            foreach ($rules as $rule){
                //dump($destinationNetwork,UtilArr::get($rule,"destination_network"));
                if($destinationNetwork == UtilArr::get($rule,"destination_network")){
                    $id = UtilArr::get($rule,"id");
                    //dump($id);
                    if($id){
                        $result = $this->client->deleteRouteTableRule($id,$routeTableId,$regionId);
                        //dump($result,$this->client->getError(false));
                        if(!$result){
                            throw new \Exception('返回数据错误:'.$this->client->getError(false));
                        }
                    }
                }
            }

            return true;
        }  catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return false;
    }

    public function routeEntryListDescribe(string $routeTableId, $regionId = ''): array
    {
        $regionId = $this->getRegionId($regionId);
        $routeTableId = trim($routeTableId);
        if (!$routeTableId) {
            return [];
        }
        $models = [];
        try {
            $data = $this->client->listRouteTableRules($routeTableId,$regionId);
            if(is_null($data)){
                throw new \Exception('返回数据错误:'.$this->client->getError(false));
            }

            //dd($data);
            foreach ($data as $datum) {
                $id = UtilArr::get($datum,"routeRuleID");
                if (!$id) {
                    continue;
                }
                $models[] = ["id" => $id, "destination_network" => UtilArr::get($datum,"destination")];
            }

        } catch (\Exception $exception) {
            $this->setError( $exception->getMessage());
        }
        return $models;
    }


    protected function formatIsp($isp, $flip = false)
    {
        $arr = [
            'BGP_PRO' => 'bgp',
            'BGP' => 'bgp',
        ];
        if ($flip) {
            $arr = array_flip($arr);
        }

        return UtilArr::get($arr, $isp, 'bgp');
    }

    protected function formatEipChargeType($type, $flip = false)
    {
        $arr = [
            'PayByBandwidth' => 'bandwidth',
            'PayByTraffic' => 'upflowc',
        ];

        if ($flip) {
            $arr = array_flip($arr);
        }
        return UtilArr::get($arr, $type, '');
    }

    protected function formatnextHopType($type, $flip = false)
    {
        $arr = [
            'Instance' => 'vm',
            'NetworkInterface' => 'enic',
            'HaVip' => 'havip',
            'NatGateway' => 'natgw',
        ];

        if ($flip) {
            $arr = array_flip($arr);
        }
        return UtilArr::get($arr, $type, '');
    }
    protected function formatEipStatus($type, $flip = false)
    {
        $arr = [
            'ACTIVE' => 'InUse',
            'DOWN' => 'Available',
            'ERROR' => 'Error',
            'UPDATING' => 'Pending',
            'BANDING_OR_UNBANGDING' => 'Associating',
            'DELETING' => 'Releasing',
            'DELETED' => 'Releasing',
            'EXPIRED' => 'Releasing',
        ];

        if ($flip) {
            $arr = array_flip($arr);
        }
        return UtilArr::get($arr, $type, '');
    }
}
